package Verifier.Cryptography;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import Verifier.GeneralUtils;
import Verifier.ByteTree.ByteTree;
import Verifier.ByteTree.Leaf;



public class CryptographyTester {

	private static void testPRG(String hashType, String seed, String expectedValue) {
		byte[] arr = ByteTree.byteStringToByteArray(seed);
		
		HashFunction f = HashFunction.HashFromByteTree(new ByteTree(new Leaf(GeneralUtils.convertStringToBytes(hashType), null)));

		PRG p = new PRG(f);
		byte[] res = p.BuildingRandom(arr, 0, 1023);
		
		String test = ByteTree.byteArrayToByteString(res);
		if (0 == expectedValue.compareToIgnoreCase(test)) {
			System.out.println("The values are equal");
		} else { System.out.println("The values aren't equal"); }
	}
	
	private static void testRandomOracle(String hashType, int outputLength, String seed, String expectedValue) {
		byte[] arr = ByteTree.byteStringToByteArray(seed);
		
		HashFunction f = HashFunction.HashFromByteTree(new ByteTree(new Leaf(GeneralUtils.convertStringToBytes(hashType), null)));
		RandomOracle r = new RandomOracle(f, outputLength);

		byte[] res;
		try {
			res = r.OutputRandomOracle(arr);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
		
		String test = ByteTree.byteArrayToByteString(res);
		if (0 == expectedValue.compareToIgnoreCase(test)) {
			System.out.println("The values are equal");
		} else { System.out.println("The values aren't equal"); }
	}
	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		
		HashFunction f = HashFunction.SHA512;
		
		byte b[] = "password".getBytes();
		
		/*for(int i=0;i<2;i++){
			b[i]=70;
		}*/
		
		System.out.println("Number of bytes = "+f.NumberOfBytes());
		
		System.out.println("The hush digest for:");
		
		StringBuffer hS=new StringBuffer();
		for (int i = 0; i < b.length; i++) {
			String hex = Integer.toHexString(0xFF & b[i]);
			if (hex.length() == 1) {
			    // could use a for loop, but we're only dealing with a single byte
			    hS.append('0');
			}
			hS.append(hex);
		}
		System.out.println(hS);
		System.out.println("is");
	
		
		byte hb[]=f.HashDigest(b);

		
	/*	StringBuffer hexString = new StringBuffer();
		for (int i=0;i<hb.length;i++) {
			hexString.append(Integer.toHexString(0xFF & hb[i]));
		}
		System.out.println("SHA" + f.NumberOfBytes()*8 +" version is "+hexString.toString());*/
		
		StringBuffer hexString = new StringBuffer();
		for (int i=0;i<hb.length;i++) {
			String hex = Integer.toHexString(0xFF & hb[i]);
			if (hex.length() == 1) {
			    // could use a for loop, but we're only dealing with a single byte
			    hexString.append('0');
			}
			hexString.append(hex);
		}
		System.out.println(hexString);
		
		
		
		PRG prg = new PRG(f);
		
		System.out.println("The number of bits in the seed is: " + prg.NumberOfBitsSeed());
		
		System.out.println("the call prg.BuildingRandom(hb,0 , f.NumberOfBytes()) returns: ");
		
		byte[] prgRetArr = prg.BuildingRandom(hb, 0, 511);
		
		StringBuffer hexS = ByteArraytoHexa(prgRetArr);

		System.out.println(hexS);
		
		RandomOracle ro = new RandomOracle(f, 76);
		
		byte[] roByteArray = ro.OutputRandomOracle(prgRetArr);
		
		StringBuffer hexRo = ByteArraytoHexa(roByteArray);
		System.out.println("blabla");
		System.out.println(hexRo);
		
		System.out.println("***************************NEW-TESTS*****************************");
		
		System.out.println("\n****TESTING HASH FUNCTIONS****");
		// testing simple words, expecting all zeros at the end of each word
		System.out.println("****SIMPLE WORDS TESTS****");
		String hashName[] = {"SHA-256", "SHA-384", "SHA-512"};
		String words[] = {"window", "hello world"};
		String hashDigestFromSite[] = {"49f8e2d8095de33f49d3385e26db3464d8268798b9e029e52a44eab60bafac76",
										"b92054a9b7bd165e3bb2b42b2581610b9cc0a71a63856284b47c6f50c58868e1be20d6aae1312979a7136d0af4f835ee",
										"a31053725d22bb483dcb76c5152b9f6aca7f5683e8bc71cf6b51c2b23a64a3b34f8466bfe688a2ecc44053cc2411c0ff4f5b4660e433f0e30400b07e031a1651",
										"eded29db0d1ab95795a7cf90bdcd2ac6ec7c5a3d449ae10a060852fece855d80",
										"17ae2b3a05ade96a48ccb654b1179989f43717a97a1d9c71d8db67609ab42661e660350baeb50be46dcdb8ed76ce6693",
										"f2a761d9657c0cfd25eee94dc80f4079b62667aedda9557e750e6d2fb647f1018d10020a993a2b2eccb7f19f74677ae1da51c5402d93cfe48573a1d1d73184bb"};
		
		int check[] = new int[words.length];
	
		int l = 0;
		for (String hash_name : hashName) {
			System.out.println(hash_name + ":");
			ByteTree hashTree = new ByteTree(ByteTree.strToByteString(hash_name));
			HashFunction hf = HashFunction.HashFromByteTree(hashTree);
			// make check all -1 to avoid "false true"
			for(int i = 0; i < check.length; i++)
				check[i] = -1;
			int i = 0;
			for (String str : words) {
				ByteTree bt = new ByteTree(ByteTree.strToByteString(str));
				System.out.println("\tString: \"" + str +"\":");
				System.out.println("\tByteTree: " + bt);
				System.out.println("\tByteString: " + bt.getByteString());
				//System.out.println("\tByteArray: " + ByteTree.byteArrayToByteString(bt.getByteArray()));
				String tempStr = ByteTree.byteArrayToByteString(hf.HashDigest(bt));
				check[i] = tempStr.compareTo(hashDigestFromSite[i*3 + l]);
				i++;
			}
			System.out.print("\t");
			for(int j = 0; j < check.length; j++)
				System.out.print(check[j] + " ");
			System.out.print("\n");
			l++;	
		}
		
		
		// testing a complex ByteTree structure
		System.out.println("****COMPLEX BYTETREE TEST****");
		ByteTree bt = new ByteTree("0000000002010000001c766572696669636174756d2e61726974686d2e4d6f64504772" +
				"6f757000000000040100000041014354c848a190b7b5fbddcd07bed36e59af5a50cc5966b" +
				"202bba0959ccc42061a2f468f87fa436451bd48d5cb333c0bb0aca763193e70c725495455" +
				"a99939276f010000004100a1aa642450c85bdafdeee683df69b72cd7ad28662cb359015dd" +
				"04ace6621030d17a347c3fd21b228dea46ae5999e05d85653b18c9f386392a4aa2ad4cc9c" +
				"93b701000000410132027413c1464af9b3ebe05f40059902857843365887f3e084e973dfd" +
				"3da198697724ac422dfce4728c2baa07760b5eae2d709bd7ff4f79d4e71fc9c2d37e26701" +
				"0000000400000001");
		
		System.out.println("byte string:\n" + bt.getByteString());

		System.out.print("SHA-256: ");
		ByteTree hashTree256 = new ByteTree(ByteTree.strToByteString("SHA-256"));
		HashFunction hf256 = HashFunction.HashFromByteTree(hashTree256);
		String temp1 = ByteTree.byteArrayToByteString(hf256.HashDigest(bt));
		System.out.println(temp1.compareTo("a0807031a1efd0563e72f3dfaad6a81899071b0228bcc4b33eb0a6a69dac8d5f"));
		
		System.out.print("SHA-384: ");
		ByteTree hashTree384 = new ByteTree(ByteTree.strToByteString("SHA-384"));
		HashFunction hf384 = HashFunction.HashFromByteTree(hashTree384);
		String temp2 = ByteTree.byteArrayToByteString(hf384.HashDigest(bt));
		System.out.println(temp2.compareTo("8fd69ff9e2ac0c630f26140c4de6c34b2dbc401be566f803f89bf2fc640ea2a22042313c3760b53589d8b8e0f6bb2928"));
		
		System.out.print("SHA-512: ");
		ByteTree hashTree = new ByteTree(ByteTree.strToByteString("SHA-512"));
		HashFunction hf512 = HashFunction.HashFromByteTree(hashTree);
		String temp3 = ByteTree.byteArrayToByteString(hf512.HashDigest(bt));
		System.out.println(temp3.compareTo("a7602cac43c79f891387ee74eba22d9adc4e58eb02d0d41a5657abeafbbdd1b337c8e55985dcbc8fe62da97adbc5893916cd71f8acfa63a70106358aec0bc683"));
		
		// Testing PRG class
		System.out.println("\n****TESTING PRG****");
		byte ba[] = (new ByteTree(ByteTree.strToByteString("window wall"))).getByteArray();
		PRG prg256 = new PRG(hf256);
		//PRG prg384 = new PRG(hf384);
		//PRG prg512 = new PRG(hf512);

		// Testing the function getBytes
//		System.out.println("Testing getBytes:");
//		//HashFunction hf256 = HashFunction.HashFromByteTree(new ByteTree(ByteTree.strToByteString("SHA-256")));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(0))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(1))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(255))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(256))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(257))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(25633))));
//		System.out.println("\t" + ByteTree.byteArrayToByteString((prg256.getBytes(12563320))));
		// All test were OK when converting to hex from dec
		
		// Testing the function getSubArray
//		System.out.println("Testing getSubArray:");
//		System.out.println("Testing length parameter:");
//		System.out.println("\t" + "\"" + ByteTree.byteArrayToByteString(ba) + "\"");
//		for (int i = 0; i <= ba.length; i++) {
//			System.out.println("\t" + "\"" + ByteTree.byteArrayToByteString((prg256.getSubArray(ba, 0, ba.length - i))) + "\"");
//		}
//		System.out.println("Testing offset parameter:");
//		System.out.println("\t" + "\"" + ByteTree.byteArrayToByteString(ba) + "\"");
//		for (int i = 0; i <= ba.length; i++) {
//			System.out.println("\t" + "\"" + ByteTree.byteArrayToByteString((prg256.getSubArray(ba, i, ba.length - i))) + "\"");
//		}
		
		//Testing the function makeSeed
//		System.out.println("Testing makeSeed:");
//		int num = 502332326;
//		String str1 = ByteTree.byteArrayToByteString(ba) + ByteTree.byteArrayToByteString(prg512.getBytes(num));
//		String str2 = ByteTree.byteArrayToByteString(prg256.makeSeed(ba, num));
//		System.out.println("\t" + str1);
//		System.out.println("\t" + str2);
//		System.out.println("\t" + str1.compareTo(str2));
		
		// Testing the function BuildingRandom
		System.out.println("\t" + prg256.BuildingRandom(ba, 1, 16) + " (null is good here)");
		StringBuilder allStr = new StringBuilder("");
		for (int i = 0; i < 3; i++) {
			byte nextBA[] = new byte[hf256.NumberOfBytes() + 4];
			ByteBuffer byteBuffer = ByteBuffer.allocate(4);
			IntBuffer intBuffer = byteBuffer.asIntBuffer();
			intBuffer.put(i);
			
			byte nextNum[] = byteBuffer.array();
			byte seed[] = hf256.HashDigest(ba);
			System.arraycopy(seed, 0, nextBA, 0, seed.length);
			nextBA[hf256.NumberOfBytes()] = nextNum[0];
			nextBA[hf256.NumberOfBytes()+1] = nextNum[1];
			nextBA[hf256.NumberOfBytes()+2] = nextNum[2];
			nextBA[hf256.NumberOfBytes()+3] = nextNum[3];
			
			allStr.append(ByteTree.byteArrayToByteString(hf256.HashDigest(nextBA)));
			//System.out.println("\t" + ByteTree.byteArrayToByteString(nextBA));
			allStr.append("|");
		}
		
		//System.out.println("\t" + ByteTree.byteArrayToByteString(Verifier.GeneralUtils.convertStringToBytes("00")));
		//System.out.println("\t" + ByteTree.byteArrayToByteString(ByteTree.byteStringToByteArray("00")));
		
		//System.out.println("\t" + ByteTree.byteArrayToByteString(hf256.HashDigest(ba)) + ByteTree.byteArrayToByteString(prg256.getBytes(0)));
		System.out.println("\t" + allStr);
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 8*32*3)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 8*14)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 8*13)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 8*12)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 8*11)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 8, 8*10)));
		System.out.println("\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 7, 8*10)));
		
		System.out.println("\n\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 10)));
		System.out.println("\n\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 11)));
		System.out.println("\n\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 12)));
		System.out.println("\n\t" + ByteTree.byteArrayToByteString(prg256.BuildingRandom(hf256.HashDigest(ba), 0, 13)));


		//49f6c46236966bc821f2f41b4ea6b4128def97b1af3cd3f261cd800775dbe041


		// Testing the RandomOracle class
		System.out.println("\n****TESTING RANDOM ORACLES****");
		int numOfBits = 31;
		RandomOracle ro256 = new RandomOracle(hf256, numOfBits);
		// Testing the function 'concat'
		ByteBuffer byteBuffer = ByteBuffer.allocate(4);
		IntBuffer intBuffer = byteBuffer.asIntBuffer();
		intBuffer.put(numOfBits);
		
		byte nextNum[] = byteBuffer.array();
		System.out.println("\t" + ByteTree.byteArrayToByteString(nextNum) + bt.getByteString());
		
		byte[] byteArr = bt.getByteArray();
		byte[] resByteArr = new byte[byteArr.length + nextNum.length];
		
		System.arraycopy(nextNum, 0, resByteArr, 0,nextNum.length);
		System.arraycopy(byteArr, 0, resByteArr, nextNum.length, byteArr.length);
		System.out.println("\t" + ByteTree.byteArrayToByteString(resByteArr));
		
		System.out.println("\t" + ByteTree.byteArrayToByteString(ro256.OutputRandomOracle(bt)));
		
		
		System.out.println("\n****TESTING PRG - Appendix Values****");
		System.out.println("\n**** SHA-256 ****");
		testPRG("SHA-256", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "70f4003d52b6eb03da852e93256b5986b5d4883098bb7973bc5318cc66637a8404a6950a06d3e3308ad7d3606ef810eb124e3943404ca746a12c51c7bf7768390f8d842ac9cb62349779a7537a78327d545aaeb33b2d42c7d1dc3680a4b23628627e9db8ad47bfe76dbe653d03d2c0a35999ed28a5023924150d72508668d244");
		System.out.println("\n**** SHA-384 ****");
		testPRG("SHA-384", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", "e45ac6c0cafff343b268d4cbd773328413672a764df99ab823b53074d94152bd27fc38bcffdb7c1dc1b6a3656b2d4819352c482da40aad3b37f333c7afa81a92b7b54551f3009efa4bdb8937492c5afca1b141c99159b4f0f819977a4e10eb5161edd4b1734717de4106f9c184a17a9b5ee61a4399dd755f322f5d707a581cc1");
		System.out.println("\n**** SHA-512 ****");
		testPRG("SHA-512", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "979043771043f4f8e0a2a19b1fbfbe5a8f076c2b5ac003e0b9619e0c45faf76747295734980602ec1d8d3cd249c165b7db62c976cb9075e35d94197c0f06e1f397a45017c508401d375ad0fa856da3dfed20847716755c6b03163aec2d9f43ebc2904f6e2cf60d3b7637f656145a2d32a6029fbda96361e1b8090c9712a48938");
		
		System.out.println("\n****TESTING RANDOM ORACLES - Appendix Values****");
		System.out.println("\n**** SHA-256 ****");
		testRandomOracle("SHA-256", 65, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "001a8d6b6f65899ba5");
		testRandomOracle("SHA-256", 261, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "1c04f57d5f5856824bca3af0ca466e283593bfc556ae2e9f4829c7ba8eb76db878");
		System.out.println("\n**** SHA-384 ****");
		testRandomOracle("SHA-384", 93, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "04713a5e22935833d436d1db");
		testRandomOracle("SHA-384", 411, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00dc086c320e38b92722a9c0f87f2f5de81b976400e2441da542d1c3f3f391e41d6bcd8297c541c2431a7272491f496b622266aa");
		System.out.println("\n**** SHA-512 ****");
		testRandomOracle("SHA-512", 111, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "28d742c34b97367eb968a3f28b6c");
		testRandomOracle("SHA-512", 579, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00a6f79b8450fef79af71005c0b1028c9f025f322f1485c2b245f658fe641d47dcbb4fe829e030b52e4a81ca35466ad1ca9be6feccb451e7289af318ddc9dae098a5475d6119ff6fe0");
	}
	
	private static StringBuffer ByteArraytoHexa(byte[] ba) {
		StringBuffer hexS = new StringBuffer();
		for (int i = 0; i < ba.length; i++) {
			String hex = Integer.toHexString(0xFF & ba[i]);
			if (hex.length() == 1) {
				// could use a for loop, but we're only dealing with a single
				// byte
				hexS.append('0');
			}
			hexS.append(hex);
		}
		return hexS;
	}
	

}
